home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Workspace / MonsterShelf / Source / IconDragView.m < prev    next >
Text File  |  1995-06-12  |  12KB  |  519 lines

  1. #import "IconDragView.h"
  2. #import "ShelfView.h"
  3. #import "Controller.h"
  4.  
  5. #import <appkit/appkit.h>
  6. #import <mach/mach.h>
  7. #import <bsd/sys/file.h>
  8.  
  9.  
  10. int mouseMoved(NXPoint *o, int n, int mask);
  11.  
  12. typedef enum { Unknown, SameDeviceOperation, WriteNotAllowed, CrossDeviceOperation } DragStatus;
  13.  
  14.  
  15. @implementation IconDragView : IconView
  16.  
  17. - initFrame:(const NXRect *) newFrame
  18.         image:anImage
  19.         data:(const void *) someData
  20.         andLength:(unsigned int) newLength
  21.         useSize:(BOOL) sizeValid
  22. {
  23.     const char *const    types[1] = {NXFilenamePboardType};
  24.  
  25.     altImage = nil;
  26.     selected = NO;
  27.  
  28.     [self registerForDraggedTypes:types count:1];
  29.     [super initFrame:newFrame image:anImage data:someData andLength:newLength
  30.             useSize:sizeValid];
  31.     
  32.     return self;
  33. }
  34.  
  35.  
  36. - free
  37. {
  38.     [altImage free];
  39.     return [super free];
  40. }
  41.  
  42.  
  43. - drawSelf:(const NXRect *) rects :(int) rectCount
  44. {
  45.     id    tempImage = image;
  46.  
  47.     image = altImage ? altImage : image;
  48.     [super drawSelf:rects :rectCount];
  49.     image = tempImage;
  50.  
  51.     return self;
  52. }
  53.  
  54.  
  55. - image
  56. {
  57.     return image;
  58. }
  59.  
  60.  
  61. - getData:(void **) aPtr andLength:(unsigned int *) aLength
  62. {
  63.     *aPtr = data;
  64.     *aLength = length;
  65.     return self;
  66. }
  67.  
  68. - mouseDown:(NXEvent *) e
  69. {
  70.     BOOL        shift, control;
  71.     NXPoint        mousePoint, offset;
  72.     unsigned int    mask = NX_LMOUSEDRAGGEDMASK|NX_LMOUSEUPMASK;
  73.     NXEvent        saveEvent;
  74.     BOOL        newState;
  75.     
  76.     shift = (e->flags & NX_SHIFTMASK) ? YES : NO;
  77.     control = (e->flags & NX_CONTROLMASK) ? YES : NO;
  78.     mousePoint = e->location;
  79.     saveEvent = *e;
  80.  
  81.     [window addToEventMask:mask];
  82.     
  83.     /*
  84.      *  Handle selection
  85.      */        
  86.     if (shift && selected)
  87.     newState = NO;
  88.     else
  89.     newState = YES;
  90.     
  91.     if (!shift && newState)
  92.     [superview deselectAll:self];
  93.  
  94.     if (newState != selected) {
  95.     selected = newState;
  96.     [self display];
  97.     }
  98.  
  99.     /*
  100.      *  Detect drag
  101.      */
  102.     if (mouseMoved(&mousePoint, 2, mask)) {
  103.         NXPoint    imagePoint;
  104.  
  105.     [self getImagePoint:&imagePoint andHilitePoint:NULL];
  106.  
  107.     offset.x = mousePoint.x - e->location.x;
  108.     offset.y = mousePoint.y - e->location.y;
  109.  
  110.     [self setState:NO];
  111.     [superview setDragView:self onEvent:&saveEvent withOffset:&offset
  112.             atLocation:&imagePoint];
  113.     return self;
  114.     }
  115.     
  116.     /*
  117.      *  If no drag, try to get workspace to do something
  118.      */
  119.     if (selected) {
  120.     BOOL    delivered = NO;
  121.     id    workspace = [Application workspace];
  122.     NXPoint    imageLoc, upperRight;
  123.     NXRect    superRect;
  124.     NXEvent    newEvent;
  125.  
  126.     [self getImagePoint:&imageLoc andHilitePoint:NULL];
  127.     [self convertPoint:&imageLoc toView:nil];
  128.     [window convertBaseToScreen:&imageLoc];
  129.     imageLoc.y++;        /* grody!  necessary to make images line up */
  130.  
  131.     [superview getFrame:&superRect];
  132.     upperRight.x = superRect.origin.x + superRect.size.width - 32;
  133.     upperRight.y = superRect.origin.y + superRect.size.height - 32;
  134.  
  135.     /*
  136.      *  Before animating the icon, check for double click.  If we
  137.      *  see a second click come in, then open the file.  Otherwise,
  138.      *  just select it in Workspace.
  139.      */
  140.     if (![NXApp peekNextEvent:NX_LMOUSEDOWNMASK into:&newEvent
  141.         waitFor:0.2 threshold:NX_MODALRESPTHRESHOLD]) {
  142.         [workspace slideImage:image from:&imageLoc to:&upperRight];
  143.         delivered = [workspace selectFile:data inFileViewerRootedAt:""];
  144.     }
  145.     else {
  146.         [workspace slideImage:image from:&imageLoc to:&upperRight];
  147.         (void) [NXApp getNextEvent:NX_LMOUSEDOWNMASK];
  148.         delivered = [workspace openFile:data];
  149.     }
  150.  
  151.     if (!delivered)
  152.         NXBeep();
  153.  
  154.     [self setState:NO];
  155.     }
  156.  
  157.     return self;
  158. }
  159.  
  160.  
  161. - (BOOL) acceptsFirstMouse
  162. {
  163.     return YES;
  164. }
  165.  
  166.  
  167. - (BOOL) acceptsFirstResponder
  168. {
  169.     return NO;
  170. }
  171.  
  172.  
  173. #define OPEN_DIR_ICON_FILE      ".opendir.tiff"
  174. #define    DEFAULT_OPEN_DIR_FILE    "/usr/lib/NextStep/Workspace.app/WM.app/openFolder.tiff"
  175. #define OPEN_HIGHLIGHT        "OpenHighlight.tiff"
  176.  
  177. - getOpenImageForDirectory:(const char *) dirname
  178. {
  179.     struct stat    st;
  180.     char    buf[MAXPATHLEN];
  181.     id        newImage = nil;
  182.  
  183.     /*
  184.      *  See if there's a .opendir.tiff
  185.      */
  186.     strncpy(buf, dirname, sizeof(buf));
  187.     strncat(buf, "/", sizeof(buf));
  188.     strncat(buf, OPEN_DIR_ICON_FILE, sizeof(buf));
  189.     if (access(buf, R_OK) == 0) {
  190.     newImage = [[NXImage allocFromZone:[self zone]] initFromFile:buf];
  191.     if (newImage)
  192.         return newImage;
  193.     }
  194.     
  195.     /*
  196.      *  Before we use the default image, see if this thing is the
  197.      *  root of a mounted file system, and use the appropriate icon
  198.      *  if it is.
  199.      */
  200.     if (stat(dirname, &st) >= 0 && st.st_ino == 2) {
  201.         NXSize size;
  202.     [image getSize:&size];
  203.         newImage = [[NXImage allocFromZone:[self zone]] initSize:&size];
  204.     if ([newImage lockFocus]) {
  205.         NXPoint    zeroPoint = {0,0};
  206.         id        highlight = [NXImage allocFromZone:[self zone]];
  207.         char        buf[MAXPATHLEN];
  208.         NXBundle   *bundle;
  209.  
  210.         bundle = [NXBundle bundleForClass:[self class]];
  211.         if ([bundle getPath:buf forResource:OPEN_HIGHLIGHT ofType:"tiff"]) {
  212.         [highlight initFromFile:buf];
  213.         [image composite:NX_COPY toPoint:&zeroPoint];
  214.         [highlight composite:NX_SOVER toPoint:&zeroPoint];
  215.         [newImage unlockFocus];
  216.         return newImage;
  217.         }
  218.     }
  219.     }
  220.  
  221.     if (access(DEFAULT_OPEN_DIR_FILE, R_OK) == 0)
  222.     newImage = [[NXImage allocFromZone:[self zone]] initFromFile:DEFAULT_OPEN_DIR_FILE];
  223.  
  224.     if (newImage != nil)
  225.     return newImage;
  226.     else
  227.     return image;
  228. }
  229.  
  230.  
  231. /*
  232.  *  Figure out the various parameters of the drag.
  233.  */
  234. - (DragStatus) dragStatusForPath:(char *) dragPath
  235. {
  236.     char        *destinationPath;
  237.     unsigned int    junk;
  238.     struct stat        st;
  239.  
  240.     [self getData:(void **) &destinationPath andLength:&junk];
  241.  
  242.     /*
  243.      *  See if we should be a dragging destination.  We need some way to
  244.      *  make this extendible.  One way would be to associate a remote
  245.      *  object with the node, and ask it what it means to drag the
  246.      *  incoming object over the one on the screen...
  247.      *
  248.      *  For now, interpret the data as a string.  Stat it to see if it's
  249.      *  a directory, and if it is, say yes subject to access constraints.
  250.      */
  251.     if (stat(destinationPath, &st) < 0 || (st.st_mode & S_IFMT) != S_IFDIR)
  252.     return WriteNotAllowed;
  253.     if (access(destinationPath, W_OK|X_OK) < 0)
  254.     return WriteNotAllowed;
  255.  
  256.     /*
  257.      *  Cool!  We're a directory.  Check the directory from which we're
  258.      *  dragging, and see it's the same as this one, or if it's contained
  259.      *  in this one.
  260.      */
  261.     if (stat(destinationPath, &st) < 0)
  262.     return Unknown;
  263.     else {
  264.     dev_t    destDev = st.st_dev;
  265.     ino_t    destIno = st.st_ino;
  266.  
  267.     if (lstat(dragPath, &st) >= 0 &&
  268.         st.st_dev == destDev && destIno == st.st_ino)
  269.         return WriteNotAllowed;
  270.     }
  271.  
  272.     /*
  273.      *  Handle the case where the dragging destination and the dragging
  274.      *  source are on different file systems.  In this case, we want to
  275.      *  copy by default, not move.
  276.      */
  277.     if (stat(destinationPath, &st) >= 0) {
  278.     dev_t    destinationDevice = st.st_dev;
  279.     if (lstat(dragPath, &st) >= 0)
  280.         if (st.st_dev != destinationDevice)
  281.         return CrossDeviceOperation;
  282.         else
  283.         return SameDeviceOperation;
  284.     }
  285.     
  286.     return Unknown;
  287. }
  288.  
  289.  
  290. /*
  291.  *  Figure out what the drag operation should be for dragging a view onto
  292.  *  another view.
  293.  */
  294. - (NXDragOperation) dragOperationForContext:(id <NXDraggingInfo>) dragInfo
  295. {
  296.     NXDragOperation    op = NX_DragOperationNone;
  297.     NXDragOperation    maskOp = [dragInfo draggingSourceOperationMask];
  298.     id            pb = [Pasteboard newName:NXDragPboard];
  299.     char        *dragPath, *nextPath;
  300.     unsigned int    junk;
  301.     BOOL        first = YES;
  302.  
  303.     [pb readType:NXFilenamePboardType data:&dragPath length:&junk];
  304.  
  305.     /*
  306.      *  We might have been asked to drag multiple files.  Loop over all of
  307.      *  them to see if they all have the same drag op.  Fail if they don't!
  308.      */
  309.     nextPath = dragPath;
  310.     do {
  311.         char        path[MAXPATHLEN];
  312.     NXDragOperation    thisOp = maskOp;
  313.  
  314.     strncpy(path, nextPath, MAXPATHLEN);
  315.     if (index(path, '\t'))
  316.         *index(path, '\t') = '\0';
  317.      
  318.     switch ([self dragStatusForPath:path]) {
  319.         case WriteNotAllowed:
  320.         case Unknown:
  321.         thisOp = NX_DragOperationNone;
  322.         break;
  323.         case CrossDeviceOperation:
  324.         if (maskOp & NX_DragOperationCopy) {
  325.             thisOp = NX_DragOperationCopy;
  326.             break;
  327.         }
  328.         /* fall through */
  329.         case SameDeviceOperation:
  330.         if (maskOp & NX_DragOperationGeneric) {
  331.             thisOp = NX_DragOperationGeneric;
  332.             break;
  333.         }
  334.         /* fall through */
  335.         default:
  336.         break;
  337.     }
  338.  
  339.     /*
  340.      *  Bail out of this operation isn't the same as the last one!
  341.      */
  342.     if (first) {
  343.         op = thisOp;
  344.         first = NO;
  345.     }
  346.     else
  347.         if (op != thisOp)
  348.         return NX_DragOperationNone;
  349.     
  350.     /*
  351.      *  Advance to next file
  352.      */
  353.     nextPath = index(nextPath, '\t');
  354.     if (nextPath == NULL)
  355.         break;
  356.     else
  357.         nextPath++;
  358.  
  359.     } while (1);
  360.  
  361.     return op;
  362. }
  363.  
  364.  
  365. /*
  366.  *  Be a dragging destination...
  367.  */
  368. - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
  369. {
  370.     lastDragOp = [self dragOperationForContext:sender];
  371.  
  372.     /*
  373.      *  If we're going to do the drag, use the alternate image.
  374.      */
  375.     if (lastDragOp != NX_DragOperationNone) {
  376.         NXSize    imageSize;
  377.     altImage = [self getOpenImageForDirectory:data];
  378.  
  379.     [image getSize:&imageSize];
  380.     [altImage setScalable:YES];
  381.     [altImage setSize:&imageSize];
  382.  
  383.     [self display];
  384.     }
  385.  
  386.     return lastDragOp;
  387. }
  388.  
  389.  
  390. - (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
  391. {
  392.     lastDragOp = [self dragOperationForContext:sender];
  393.     return lastDragOp;
  394. }
  395.  
  396.  
  397. - draggingExited:(id <NXDraggingInfo>)sender
  398. {
  399.     /*
  400.      *  Reset image to original image.
  401.      */
  402.     [altImage free];
  403.     altImage = nil;
  404.     [self display];
  405.     [window flushWindow];
  406.     return self;
  407. }
  408.  
  409.  
  410. /*
  411.  *  Eat the result...
  412.  */
  413. - (BOOL) prepareForDragOperation:sender
  414. {
  415.     return YES;
  416. }
  417.  
  418. - (BOOL) performDragOperation:(id <NXDraggingInfo>)sender
  419. {
  420.     return YES;
  421. }
  422.  
  423.  
  424. - concludeDragOperation:(id <NXDraggingInfo>)sender
  425. {
  426.     id            workspace = [Application workspace];
  427.     id            pb = [Pasteboard newName:NXDragPboard];
  428.     const char        *wsmOp;
  429.     char        *pbData;
  430.     unsigned int    pbLength;
  431.  
  432.     switch (lastDragOp) {
  433.     case NX_DragOperationGeneric:
  434.         wsmOp = WSM_MOVE_OPERATION;
  435.         break;
  436.     case NX_DragOperationLink:
  437.         wsmOp = WSM_LINK_OPERATION;
  438.         break;
  439.     case NX_DragOperationCopy:
  440.         wsmOp = WSM_COPY_OPERATION;
  441.         break;
  442.     default:
  443.         return NO;
  444.     }
  445.  
  446.     [pb readType:NXFilenamePboardType data:&pbData length:&pbLength];
  447.     [workspace performFileOperation:wsmOp source:"/" destination:data
  448.                 files:pbData options:NULL];
  449.  
  450.     return [self draggingExited:sender];
  451. }
  452.  
  453.  
  454. - (BOOL) isOnRemovableMedia
  455. {
  456.     struct stat    st;
  457.     char    *mountedList = NULL;
  458.     id        workspace = [Application workspace];
  459.  
  460.     if (stat(data, &st) < 0)
  461.         device = 0;
  462.     else
  463.     device = st.st_dev;
  464.  
  465.     removable = NO;
  466.     if ([workspace respondsTo:@selector(getMountedRemovableMedia:)] &&
  467.         [workspace getMountedRemovableMedia:&mountedList])
  468.     [self perform:@selector(isOnRemovableDevice:) withPaths:mountedList];
  469.  
  470.     if (mountedList)
  471.     free(mountedList);
  472.  
  473.     return removable;
  474. }
  475.  
  476.  
  477. - isOnRemovableDevice:(const char *)path
  478. {
  479.     struct stat    st;
  480.     
  481.     if (path && stat(path, &st) >= 0)
  482.     removable |= (device == st.st_dev);
  483.  
  484.     return self;
  485. }
  486.  
  487.  
  488. @end
  489.  
  490.  
  491. /*
  492.  * Returns true if mouse is dragged more than n pixels from 'o',
  493.  * false if the mouse button is lifted.  If the mouse button is
  494.  * lifted, the mouseUp event is not consumed.
  495.  */
  496. int
  497. mouseMoved(NXPoint *o, int n, int mask)
  498. {
  499.     NXEvent e;
  500.     NXPoint p;
  501.     float   dx, dy;
  502.  
  503.     do {
  504.     [NXApp peekNextEvent:mask into:&e waitFor:0x7fffffff
  505.            threshold:NX_MODALRESPTHRESHOLD];
  506.     p = e.location;
  507.     if (e.type == NX_LMOUSEUP)
  508.         break;
  509.  
  510.     (void) [NXApp getNextEvent:mask];       /* throw it away */
  511.     dx = abs(p.x - o->x);
  512.     dy = abs(p.y - o->y);
  513.  
  514.     } while ((dy < (float) n) && (dx < (float) n));
  515.     *o = p;
  516.  
  517.     return e.type != NX_LMOUSEUP;
  518. }
  519.